using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace GameOfLife
{
    public class Board
    {
        bool[,] cellMatrix;
        Random rand = new Random();
        
        /// <summary>
        /// Creates a cell matrix with all dead cells
        /// </summary>
        /// <param name="sizeX">The number of cells across</param>
        /// <param name="sizeY">The number of cells down</param>
        public Board(int sizeX, int sizeY)
        {
            cellMatrix = new bool[sizeX,sizeY];
        }


        /// <summary>
        /// Creates a cell matrix with all dead cells
        ///  except for the cell specified by the coordinates
        ///  of the POINTS parameter
        /// 
        ///   For example:
        ///    if points = [ {1,2} , {0,0}, {3,2} ]
        ///       sizeX = 4
        ///       sizeY = 3
        ///   
        ///    the cell matrix would look like
        ///      
        ///           +----+
        ///           |1000|
        ///           |0000|       
        ///           |0101|
        ///           +----+
        /// 
        /// 
        /// </summary>
        /// <param name="points">the coordinates of the cells that are alive</param>
        /// <param name="sizeX">the number of cells across</param>
        /// <param name="sizeY">the number of cells down</param>
        public Board(Point[] points, int sizeX, int sizeY)
        {
            cellMatrix = new bool[sizeX, sizeY];
            /** For each point mark cell "living" **/
            foreach(Point p in points)
            {
                cellMatrix[p.X, p.Y] = true;
            }
        }


        /// <summary>
        /// Creates a cell matrix based on the contents
        ///  of a text file
        /// 
        /// The first two lines should give the demensions
        ///  of the cell matrix. The first line is the number
        ///  of cells across and the second line is the number
        ///  of cells down
        /// The remaing lines define the cell matrix. 0
        ///  represents a dead cell and 1 represents a
        ///  living cell
        /// 
        ///
        /// 
        ///   For exmaple:
        /// 
        ///     4                 +----+
        ///     3      makes      |1000|
        ///     1000    === \     |0000|       
        ///     0000    === /     |0101|
        ///     0101              +----+
        ///
        /// 
        /// </summary>
        /// <param name="filename">the name of the file</param>
        public Board(string filename)
        {
            /** Fix Me **/
            FileInfo file = new FileInfo(filename);
            StreamReader stream = null;
            string line = "";
            int x, y;

            try
            {
                stream = file.OpenText();
                
                x = Convert.ToInt32(stream.ReadLine()); // read first line in as int for x
                y = Convert.ToInt32(stream.ReadLine()); // read next line in as int for y
                
                cellMatrix = new bool[x, y]; // create cellMatrix

                /* Read remaining lines and fill in cellMatrix*/                
                int row = 0;
                while(null != (line = stream.ReadLine()) )
                {
                    int col = 0;
                    foreach(char c in line)
                    {
                        if( c == '1')
                        {
                            cellMatrix[col, row] = true;
                        }
                        col++;
                    }
                    row++;
                }
            }
            catch(FileNotFoundException)
            {
                Console.WriteLine("Error: Failed to create board - file: " + filename + " not found.");
            }
            catch(IOException)
            {
                Console.WriteLine("Error: Failed to create board - IOException.");
            }
            catch(System.IndexOutOfRangeException)
            {
                Console.WriteLine("Error: Failed to create board - IndexOutOfRangeException.");
            }
            finally
            {
                if(stream != null)
                {
                    stream.Close();
                }
            }
        }



        /// <summary>
        /// This method sets all of the cells
        ///  in cellMatrix to dead (false)
        /// </summary>
        public void ClearBoard()
        {
            /** Fix Me **/
		for(int x = 0; x <= cellMatrix.GetUpperBound(0);++x)
            	{
			for(int y = 0; y <= cellMatrix.GetUpperBound(1);++y)
			{
                		cellMatrix[x, y] = false;
			}
            	}
        }


        /// <summary>
        /// This creates a random cell matrix
        ///  by giving each cell in cellMatrix an 
        ///  equal chance of being alive (true)
        ///  or dead (false)
        /// </summary>
        public void RandomBoard()
        {
            /** Fix Me **/
		for(int x = 0; x <= cellMatrix.GetUpperBound(0);++x)
            	{
			for(int y = 0; y <= cellMatrix.GetUpperBound(1);++y)
			{
                		if (rand.Next(0, 2) == 0) 
				{
					cellMatrix[x, y] = false;
            			}
            			else 
				{
                			cellMatrix[x, y] = true;
				}       			
			}
            	} 
        }



        /// <summary>
        /// 
        /// This method updates the cellMatrix by updating it
        ///  to the next time step
        /// 
        ///  This is done by visiting each cell in cellMatrix to count the
        ///   number of its neighbors that are living, then
        ///   using that number and the ruleset to deterning if 
        ///   that cell should be dead
        ///   or alive in the updated cellMatrix
        /// 
        /// Hint:
        /// Use RuleSet's NextState() method
        ///  and Board's NumNeighbors() method
        /// 
        /// </summary>
        /// <param name="ruleset">
        /// the ruleset used to determine how many neighbors
        ///  need to be alive for a cell to be alive during
        ///  the next time step
        /// </param>
        public void Step(RuleSet ruleset)
        {
            /** Fix Me **/
		bool [,] tempMatrix = new bool[SizeX(),SizeY()];
		for(int x = 0; x <= cellMatrix.GetUpperBound(0);++x)
            	{
			for(int y = 0; y <= cellMatrix.GetUpperBound(1);++y)
			{
                		tempMatrix[x, y] = ruleset.NextState(cellMatrix[x, y],NumNeighbors(x,y));    
			}
            	}
		cellMatrix = tempMatrix;
		
		
        }

        /// <summary>
        /// This method counts the number of living cells
        ///  that are adjacent to a given cell
        /// 
        ///  Each cells has 8 adjacent cells
        ///        123
        ///        8*4
        ///        765
        /// 
        ///  Note: There are no boundaries because the matrix
        ///   is toroidally wrapped.  This means that the
        ///   left most cells are adjacent to the right most
        ///   cells and the top cells are adjacent to the
        ///   bottom cells.
        /// 
        /// parameters i and j specify the location
        ///  of the given cell in cellMatrix
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public int NumNeighbors(int i, int j)
        {
            int count = 0;
            for (int x = -1; x <= 1; x++)
            {
                for (int y = -1; y <= 1; y++)
                {
                    if ((x == 0) && (y == 0)) continue;
                    int xCoord = (i + x + SizeX()) % SizeX();
                    int yCoord = (j + y + SizeY()) % SizeY();
                    if (GetCell(xCoord, yCoord)) count++;
                }
            }
            return count;
        }


        /// <summary>
        /// This method is used for getting
        ///  the dimensions of the cellMatrix
        /// 
        /// Hint: this should be a one line function
        ///  if you use bool[][]'s GetLength() method
        /// </summary>
        /// <returns>the number of cells across the cell Matrix</returns>
        public int SizeX()
        {
            /** Fix Me **/
            return cellMatrix.GetLength(0);
        }

        /// <summary>
        /// This method is used for getting
        ///  the dimensions of the cellMatrix
        /// 
        /// Hint: this should be a one line function
        ///  if you use bool[][]'s GetLength() method
        /// </summary>
        /// <returns>the number of cells down the cell Matrix</returns>
        public int SizeY()
        {
            /** Fix Me **/
            return cellMatrix.GetLength(1);
        }

        

        /// <summary>
        /// This method is called to determine
        ///  if a given cell is dead or alive
        /// 
        /// parameters x and y specify the location
        ///  of the given cell
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>true if the cell is alive, false if the cell is dead</returns>
        public bool GetCell(int x, int y)
        {
            return cellMatrix[x, y];
        }
    }
}
